using System;
using System.ComponentModel;
using System.Net;
using Meebey.SmartIrc4net;

namespace IrcBot.IrcSender
{
    internal class Bot
    {
        private IrcClient _ircClient;
        private readonly BackgroundWorker _listenWorker;

        internal string Name { get; set; }
        internal string Channel { get; set; }
        internal IPEndPoint IrcServer { get; set; }
        

        internal Bot(string name, string channel, IPEndPoint ircServer)
        {
            _ircClient = new IrcClient();
            Name = name;
            Channel = channel;
            IrcServer = ircServer;

            Connect();

            // start listening, library must listen in order to send messages (weird, I know...)
            _listenWorker = new BackgroundWorker();
            _listenWorker.DoWork += Listen;
            _listenWorker.WorkerSupportsCancellation = true;
            _listenWorker.RunWorkerAsync();

        }

        internal void SendMessage(string message)
        {
            try
            {
                _ircClient.SendMessage(SendType.Message, Channel, message);
            }
            catch (ConnectionException ex)
            {
                Disconnect("Send error, quitting");
                throw new MessageSendException("Failed to send message to IRC, connection was not in a valid state.", ex);
            }
            catch (BotConnectionException ex)
            {
                throw new MessageSendException("Failed to send message to IRC, could not connect to IRC server.", ex);
            }
            catch (Exception ex)
            {
                Disconnect("Send error, quitting");
                throw new MessageSendException("Failed to send message to IRC, unknown exception was thrown.", ex);
            }
        }

        #region Listen thread (spawned by client connection thread)

        private void Listen(object sender, DoWorkEventArgs e)
        {
            _ircClient.Listen(true);
        }

        #endregion


        private void Connect()
        {
            try
            {
                if (!_ircClient.IsConnected)
                {
                    _ircClient.Encoding = System.Text.Encoding.UTF8;
                    _ircClient.SendDelay = 200;
                    _ircClient.ActiveChannelSyncing = true;
                    _ircClient.Connect(IrcServer.Address.ToString(), IrcServer.Port);
                    _ircClient.Login(Name, string.Format("Bot {0}", Name));
                }

                if (!_ircClient.IsJoined(Channel))
                {
                    _ircClient.RfcJoin(Channel);
                }
            }
            catch (ConnectionException ex)
            {
                Disconnect(null);
                throw new BotConnectionException(string.Format("Failed to connect to IRC server on [{0}:{1}]", IrcServer.Address, IrcServer.Port), ex);
            }
        }

        private void Disconnect(string message)
        {
            if (_ircClient != null && _ircClient.IsConnected)
            {
                foreach (var channel in _ircClient.JoinedChannels)
                {
                    _ircClient.RfcPart(channel, message);
                }
                _listenWorker.CancelAsync();
                _ircClient.RfcQuit();
                _ircClient.Disconnect();
            }
        }


        #region Clean up methods

        internal void Dispose(string message)
        {
            Disconnect(message);
            _ircClient = null;

            // do not suppress finalise, IrcClient does not have a Dispose method and we can't be
            // sure it releases all unmanaged resources (e.g. Sockets) properly...
        }

        internal void Dispose()
        {
            Dispose(null);
        }

        #endregion
    }
}